`timescale       1ns/1ps
`default_nettype none

//将要显示的数据从SDRAM读取到缓冲区

/* NOTE:
* 行控：
* - 同时打开4个bank进行处理,4个bank轮流读取
* - 每次读出1个port的8个pixel
*
* 列控：
* - 同时打开4个bank进行处理,4个bank轮流读取
* - 每次读出8个port的1个pixel
* - 先依次读出8个port的8个pixel，再读取下一个8个port的8个pixel，直到port读完
* - 每8个port的8个pixel交织后，依次输出每个port的8个pixel
*/

`define ENABLE_COL_CTRL

//***********************************************************
module cxy_pixel_reader(
    // system signal
    input  wire         I_sclk,      // 125M
    input  wire         I_sdram_clk, // now is 125M, could be bigger
    input  wire         I_rst_n,
    // config
    input  wire [1:0]   I_cfg_scan_mode,
    input  wire [5:0]   I_cfg_color_sel,    // RGB选择
    input  wire [1:0]   I_cfg_box_dir,      // 箱体方向
    input  wire [7:0]   I_cfg_vport_num,    // 虚拟数据组数
    input  wire [31:0]  I_cfg_vport_mask,   // 虚拟数据组中有效组标识
    input  wire         I_cfg_force_en,     // 强制读出的第几bit为1，其余bit为0
    input  wire [4:0]   I_cfg_force_bit,    // 0:全为0; 1:只bit0为1; 2:只bit1为1; ...
    // read request
    input  wire         I_read_req,         // 读请求
    output wire         O_read_busy,        // 读忙碌
    input  wire [1:0]   I_read_sdram_sel,   // 读取SDRAM分块地址
    input  wire [3:0]   I_read_bit_sel,     // 读取的bit选择
    input  wire [5:0]   I_read_scan_id,     // 读取的scan id
    input  wire [4:0]   I_read_port_max,    // 读取的最大port id (加1后表示实际的port数,加1后必须为4的倍数)
    input  wire [9:0]   I_read_pixel_count, // 读取的像素数，总是8的倍数，最多512
    input  wire         I_read_buf_index,

    // scan map
    output wire         O_scan_map_rden,
    output wire [5:0]   O_scan_map_addr,
    input  wire [5:0]   I_scan_map_q,
    // port map
    output wire         O_port_map_rden,
    output wire [7:0]   O_port_map_addr,
    input  wire [4:0]   I_port_map_q,
    // ram interface
    output wire         O_ram_wclk,
    output wire [7:0]   O_ram_wren,         // 每bit控制一个ram块，一共8个ram块
    output wire [8:0]   O_ram_addr,
    output wire [23:0]  O_ram_data,
    // buf interface
    output wire         O_buf_frm_start,
    output wire         O_buf_sel,
    output wire         O_buf_vld,
    output wire [4:0]   O_buf_port,
    output wire         O_buf_port_end,
    output wire [23:0]  O_buf_data,

    // sdram mux
    output wire         O_mux_req,
    input  wire         I_mux_ack,
    output wire         O_mux_cs_n,
    output wire         O_mux_ras_n,
    output wire         O_mux_cas_n,
    output wire         O_mux_we_n,
    output wire [1:0]   O_mux_ba,
    output wire [10:0]  O_mux_addr,
    output wire [31:0]  O_mux_dq_out,
    input  wire [31:0]  I_mux_dq_in,
    output wire         O_mux_dq_oe,
    output wire [3:0]   O_mux_dqm
    );
//***********************************************************
`include "sdram_common.vh" 

// fsm
localparam [4:0]
    IDLE    = 0,
    REQ_MUX = 1,
    ACT0    = 2,
    ACT1    = 3,
    ACT2    = 4,
    ACT3    = 5,
    NOP0    = 6,
    NOP1    = 7,
    NOP2    = 8,
    NOP3    = 9,
    RD0     = 10,
    RD1     = 11,
    RD2     = 12,
    RD3     = 13,
    PRE     = 14,
    WAIT    = 15,
    LOOP    = 16;

// box direction
localparam [1:0]
    LANDSCAPE = 0, // 横向
    PORTRAIT0 = 1, // 纵向，第一个端口在左侧
    PORTRAIT1 = 2; // 纵向，第一个端口在右侧

localparam
    RL = 4 + SD_MR_CL; // read data latency (SD_MR_CL=3)

//***********************************************************
//------------------------Local signal-------------------
// read request (@I_sclk)
reg         busy;
reg         req_sr;
reg  [2:0]  done_sr;

reg  [1:0]  read_sdram_sel;
reg  [3:0]  read_bit_sel;
reg  [4:0]  read_port_max;
reg  [9:0]  read_pixel_count;
reg         read_buf_index;
reg  [8:0]  read_pixel_count_minus_1;

reg         scan_q_valid;
reg  [5:0]  read_scan_id;
reg         landscape_mode;

// fsm (@I_sdram_clk)
reg  [4:0]  state;
reg  [4:0]  last_state;
reg  [3:0]  start_sr;
reg  [1:0]  end_sr;
reg  [2:0]  wait_cnt;

reg  [2:0]  port_id;    // 每个数表示4个port
reg         last_port;
reg         change_row;
reg  [5:0]  pixel_id;   // 每个数表示8个pixel
reg  [6:0]  pixel_cnt;
reg         last_pixel;

// sdram mux
reg         mux_req;
reg  [3:0]  mux_cmd;
reg  [1:0]  mux_ba;
reg  [10:0] mux_addr;

reg  [RL+8:0] read_sr;

// port ram
reg  [4:0]  real_port_index;
reg  [4:0]  virtual_port_index;
reg  [4:0]  virtual_port_index_tmp0;
reg  [4:0]  virtual_port_index_tmp1;
reg  [5:0]  pixel_index;

// data repack
reg  [31:0] d;
reg  [7:0]  r;
reg  [7:0]  g;
reg  [7:0]  b;
wire [23:0] word1;

reg  [7:0]  h_ram_wren;
reg  [8:0]  h_ram_addr;
reg  [23:0] h_ram_data;

reg  [7:0]  v_ram_wren;
reg  [8:0]  v_ram_addr;
reg  [23:0] v_ram_data;

// 列控
reg  [2:0]  v_row_pixel_cnt;
reg         v_change_port;
reg  [1:0]  v_port_id;
reg  [8:0]  v_tal_pixel_cnt;
reg         v_change_row;
reg         v_last_pixel;

reg  [2:0]  v_row_pixel_cnt_1;
reg         v_change_port_1;
reg  [1:0]  v_port_id_1;
reg  [8:0]  v_tal_pixel_cnt_1;
reg  [8:0]  v_tal_pixel_cnt_1_tmp0;
reg  [8:0]  v_tal_pixel_cnt_1_tmp1;

reg  [3:0]  wr_reg_cnt;
reg  [3:0]  rd_reg_cnt;
reg  [23:0] d0,d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15;
reg  [4:0]  v_real_port_index;
reg  [2:0]  v_virtual_port_index;

wire        v_port_map_rden;
wire [7:0]  v_port_map_addr;

wire        h_port_map_rden;
wire [7:0]  h_port_map_addr;
//***********************************************************
reg         buf_frm_start;

reg         h_buf_vld;
reg  [4:0]  h_buf_port;
reg         h_buf_port_end;
reg  [23:0] h_buf_data;

reg         v_buf_vld;
reg  [4:0]  v_buf_port;
reg         v_buf_port_end;
reg  [23:0] v_buf_data;

reg  [4:0]  v_buf_port_index;
//***********************************************************
//O_read_busy
assign O_read_busy = busy;

//busy
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        busy <= 'b0;
    else if(busy==0 && I_read_req==1)
        busy <= 1'b1;
    else if(done_sr[2:1]==2'b01)
        busy <= 'b0;

//req_sr
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        req_sr <= 'b0;
    else if(busy==0 && I_read_req==1)
        req_sr <= 1'b1;
    else
        req_sr <= 'b0;

//done_sr[2:0]
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        done_sr <= 'b0;
    else
        done_sr <= {done_sr[1:0],end_sr[1]};

//command buffer
always@(posedge I_sclk)
    if(busy==0 && I_read_req==1)
    begin
        read_sdram_sel           <= I_read_sdram_sel;
        read_bit_sel             <= I_read_bit_sel;
        read_port_max            <= I_read_port_max;
        read_buf_index           <= I_read_buf_index;
        read_pixel_count         <= I_read_pixel_count;
        read_pixel_count_minus_1 <= I_read_pixel_count - 1'b1;
    end
//***********************************************************
// 从外部RAM中读取当前要读取第几扫的数据
//O_scan_map_rden
//O_scan_map_addr[5:0]
assign O_scan_map_rden = I_read_req && !busy;
assign O_scan_map_addr = I_read_scan_id;

//scan_q_valid
always@(posedge I_sclk or negedge I_rst_n)
    if(!I_rst_n)
        scan_q_valid <= 'b0;
    else
        scan_q_valid <= O_scan_map_rden;

//read_scan_id[5:0]
always@(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        read_scan_id <= 'b0;
    else if(scan_q_valid==1)
        read_scan_id <= I_scan_map_q;

//landscape_mode:  1 - 行控，0 - 列控
always@(posedge I_sclk)
`ifdef ENABLE_COL_CTRL
    landscape_mode <= (I_cfg_box_dir == LANDSCAPE);
`else
    landscape_mode <= 1'b1;
`endif
//***********************************************************
//state[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        state <= IDLE;
    else
    case(state)
        IDLE:
            if(start_sr[3]==1)
                state <= REQ_MUX;

        REQ_MUX:
            if(I_mux_ack==1)
                state <= ACT0;

        //----------------------------
        ACT0:   state <= NOP0;
        NOP0:   state <= ACT1;
        ACT1:   state <= NOP1;
        NOP1:   state <= ACT2;
        ACT2:   state <= NOP2;
        NOP2:   state <= ACT3;
        ACT3:   state <= RD0;

        RD0:    state <= RD1;
        RD1:    state <= RD2;
        RD2:    state <= RD3;

        RD3:
            if(   landscape_mode==1 && (  change_row==1 ||   last_pixel==1)     //行控
               || landscape_mode==0 && (v_change_row==1 || v_last_pixel==1))    //列控
                state <= NOP3;
            else
                state <= RD0;

        NOP3:   state <= PRE;
        PRE:    state <= WAIT;

        WAIT:
            if(wait_cnt==DELAY_RP-2)
                state <= LOOP;

        LOOP:
            if(   landscape_mode==1 &&   last_pixel==1 
               || landscape_mode==0 && v_last_pixel==1)
                state <= IDLE;
            else
                state <= REQ_MUX;

        //----------------------------
        default:state <= IDLE;
    endcase

//last_state[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        last_state <= IDLE;
    else
        last_state <= state;
//***********************************************************
//start_sr[3:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        start_sr <= 'b0;
    else if((busy==0 && I_read_req==1) || req_sr==1)
        start_sr <= {start_sr[2:0], 1'b1};
    else
        start_sr <= {start_sr[2:0], 1'b0};

//end_sr[1:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        end_sr <= 'b0;
    else if(state==IDLE && last_state==LOOP)
        end_sr <= 2'b11;
    else
        end_sr <= end_sr << 1;

//wait_cnt[2:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        wait_cnt <= 'b0;
    else if(state==WAIT)
        wait_cnt <= wait_cnt + 1'b1;
    else
        wait_cnt <= 'b0;
//***********************************************************
//port_id[2:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        port_id <= 'b0;
    else if(state==IDLE || last_port==1)
        port_id <= 'b0;
    else if(state==RD3)
        port_id <= port_id + 1'b1;

//last_port
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        last_port <= 'b0;
    else if(state==RD2 && port_id==read_port_max[4:2])
        last_port <= 1'b1;
    else
        last_port <= 'b0;

//change_row
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        change_row <= 'b0;
    else if(state==RD2 && port_id==read_port_max[4:2])
    begin
        if(I_cfg_scan_mode==2)
            change_row <= (pixel_id[3:0]==4'b1111);
        else
            change_row <= (pixel_id[2:0]==3'b111);
    end
    else
        change_row <= 1'b0;

//pixel_id[5:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        pixel_id <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        pixel_id <= 'b0;
    else if(last_port==1)
        pixel_id <= pixel_id + 1'b1;

//pixel_cnt[6:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        pixel_cnt <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        pixel_cnt <= read_pixel_count[9:3];
    else if(last_port==1)
        pixel_cnt <= pixel_cnt - 1'b1;

//last_pixel
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        last_pixel <= 'b0;
    else if(state==IDLE)
        last_pixel <= 'b0;
    else if(state==RD2 && port_id==read_port_max[4:2] && pixel_cnt==1)
        last_pixel <= 1'b1;
//***********************************************************
assign O_mux_req    = mux_req;
assign O_mux_cs_n   = mux_cmd[3];
assign O_mux_ras_n  = mux_cmd[2];
assign O_mux_cas_n  = mux_cmd[1];
assign O_mux_we_n   = mux_cmd[0];
assign O_mux_ba     = mux_ba;
assign O_mux_addr   = mux_addr;
assign O_mux_dq_out = 'b0;
assign O_mux_dq_oe  = 'b0;
assign O_mux_dqm    = 'b0;

//mux_req
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        mux_req <= 'b0;
    else if(state==REQ_MUX)
        mux_req <= 1'b1;
    else if(state==LOOP)
        mux_req <= 'b0;

//mux_cmd[3:0]
always@(posedge I_sdram_clk)
    case(state)
        ACT0,ACT1,ACT2,ACT3:    mux_cmd <= SD_CMD_ACT;
        RD0,RD1,RD2,RD3:        mux_cmd <= SD_CMD_READ;
        PRE:                    mux_cmd <= SD_CMD_PRE;
        default:                mux_cmd <= SD_CMD_NOP;
    endcase

//mux_ba[1:0]
always@(posedge I_sdram_clk)
    case(state)
        ACT0,RD0:   mux_ba <= read_bit_sel[3:2] + 2'd0;
        ACT1,RD1:   mux_ba <= read_bit_sel[3:2] + 2'd1;
        ACT2,RD2:   mux_ba <= read_bit_sel[3:2] + 2'd2;
        ACT3,RD3:   mux_ba <= read_bit_sel[3:2] + 2'd3;
        default:    mux_ba <= 'b0;
    endcase

//mux_addr[10:0]
always@(posedge I_sdram_clk)
    case(state)
        // 打开某一行
        ACT0,ACT1,ACT2,ACT3:
            case({landscape_mode,I_cfg_scan_mode})
                3'b1_00:  mux_addr <= {read_sdram_sel[1:0],read_bit_sel[3:2],read_scan_id[3:0],pixel_id[5:3]};
                3'b1_01:  mux_addr <= {read_sdram_sel[1:0],read_bit_sel[3:2],read_scan_id[4:0],pixel_id[4:3]};
                3'b1_10:  mux_addr <= {read_sdram_sel[1:0],read_bit_sel[3:2],read_scan_id[4:0],pixel_id[5:4]};

                3'b0_00:  mux_addr <= {read_sdram_sel[1:0],read_bit_sel[3:2],read_scan_id[3:0],v_tal_pixel_cnt[8:6]};
                3'b0_01:  mux_addr <= {read_sdram_sel[1:0],read_bit_sel[3:2],read_scan_id[4:0],v_tal_pixel_cnt[7:6]};
                3'b0_10:  mux_addr <= {read_sdram_sel[1:0],read_bit_sel[3:2],read_scan_id[4:0],v_tal_pixel_cnt[8:7]};
            endcase

        // 读取某一列
        RD0,RD1,RD2,RD3:
            case({landscape_mode,I_cfg_scan_mode})
                3'b1_00,
                3'b1_01:  mux_addr <= {pixel_id[2:0],port_id[2:0],read_bit_sel[1:0]};
                3'b1_10:  mux_addr <= {pixel_id[3:0],port_id[1:0],read_bit_sel[1:0]};

                3'b0_00,
                3'b0_01:  mux_addr <= {v_tal_pixel_cnt[5:3],v_port_id[1:0],v_row_pixel_cnt[2],read_bit_sel[1:0]};
                3'b0_10:  mux_addr <= {v_tal_pixel_cnt[6:3],v_port_id[0  ],v_row_pixel_cnt[2],read_bit_sel[1:0]};
            endcase

        PRE:    mux_addr <= 11'h400;

        default:mux_addr <= 'b0;
    endcase
//***********************************************************
//read_sr[RL+8:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        read_sr <= 'b0;
    else if(state==RD0 || state==RD1 || state==RD2 || state==RD3)
        read_sr <= {read_sr[RL+7:0],1'b1};
    else
        read_sr <= {read_sr[RL+7:0],1'b0};
//***********************************************************
//h_port_map_rden
//h_port_map_addr[7:0]
assign h_port_map_rden = read_sr[RL-2];
assign h_port_map_addr = {3'b100,virtual_port_index[4:0]};

//real_port_index[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        real_port_index <= 'b0;
    else if(read_sr[RL-1]==1)
        real_port_index <= I_port_map_q;
      //real_port_index <= virtual_port_index_tmp0;    // for test

//virtual_port_index[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        virtual_port_index <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        virtual_port_index <= 'b0;
    else if(read_sr[RL-2]==1)
    begin
        if(virtual_port_index=={read_port_max[4:2],2'b11})
            virtual_port_index <= 'b0;
        else
            virtual_port_index <= virtual_port_index + 1'b1;
    end

//virtual_port_index_tmp0/1[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
    begin
        virtual_port_index_tmp0 <= 'b0;
        virtual_port_index_tmp1 <= 'b0;
    end
    else
    begin
        virtual_port_index_tmp0 <= virtual_port_index;
        virtual_port_index_tmp1 <= virtual_port_index_tmp0;
    end

//pixel_index[5:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        pixel_index <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        pixel_index <= 'b0;
    else if(read_sr[RL]==1)
    begin
        if(virtual_port_index_tmp1=={read_port_max[4:2],2'b11})
            pixel_index <= pixel_index + 1'b1;
    end
//***********************************************************
//d[31:0]
always@(posedge I_sdram_clk)
    if(I_cfg_force_en==1)
    begin
        if(read_bit_sel==I_cfg_force_bit-1)
            d <= 32'hFFFF_FFFF;
        else
            d <= 32'b0;
    end
    else
        d <= I_mux_dq_in;

//r[7:0]
always@(*)
    case(I_cfg_color_sel[1:0])
        2'd0:       r = {d[28],d[24],d[20],d[16],d[12],d[8 ],d[4],d[0]};
        2'd1:       r = {d[29],d[25],d[21],d[17],d[13],d[9 ],d[5],d[1]};
        default:    r = {d[30],d[26],d[22],d[18],d[14],d[10],d[6],d[2]};
    endcase

//g[7:0]
always@(*)
    case(I_cfg_color_sel[3:2])
        2'd0:       g = {d[28],d[24],d[20],d[16],d[12],d[8 ],d[4],d[0]};
        2'd1:       g = {d[29],d[25],d[21],d[17],d[13],d[9 ],d[5],d[1]};
        default:    g = {d[30],d[26],d[22],d[18],d[14],d[10],d[6],d[2]};
    endcase

//b[7:0]
always@(*)
    case(I_cfg_color_sel[5:4])
        2'd0:       b = {d[28],d[24],d[20],d[16],d[12],d[8 ],d[4],d[0]};
        2'd1:       b = {d[29],d[25],d[21],d[17],d[13],d[9 ],d[5],d[1]};
        default:    b = {d[30],d[26],d[22],d[18],d[14],d[10],d[6],d[2]};
    endcase

//RGB重排序
//word1[23:0]
assign word1 = {b[7],g[7],r[7],
                b[6],g[6],r[6],
                b[5],g[5],r[5],
                b[4],g[4],r[4],
                b[3],g[3],r[3],
                b[2],g[2],r[2],
                b[1],g[1],r[1],
                b[0],g[0],r[0]};
//***********************************************************
//h_ram_wren[7:0]
//h_ram_addr[8:0]
//h_ram_data[23:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        h_ram_wren <= 'b0;
    else if(read_sr[RL]==1)
    begin
        h_ram_wren <= (1'b1 << real_port_index[4:2]);
        h_ram_addr <= {read_buf_index,real_port_index[1:0],pixel_index[5:0]};
        h_ram_data <= word1;
    end
    else
        h_ram_wren <= 'b0;
//***********************************************************
//v_row_pixel_cnt[2:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_row_pixel_cnt <= 'b0;
    else if(state==IDLE || v_change_port==1)
        v_row_pixel_cnt <= 'b0;
    else if(state==RD0 || state==RD1 || state==RD2 || state==RD3)
        v_row_pixel_cnt <= v_row_pixel_cnt + 1'b1;

//v_change_port
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_change_port <= 'b0;
    else if(state==RD2 && v_row_pixel_cnt==3'd6)
        v_change_port <= 1'b1;
    else
        v_change_port <= 'b0;

//v_port_id[1:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_port_id <= 'b0;
    else if(state==IDLE)
        v_port_id <= 'b0;
    else if(v_change_port==1)
    begin
        if(v_port_id==read_port_max[4:3])
            v_port_id <= 'b0;
        else
            v_port_id <= v_port_id + 1'b1;
    end

//v_tal_pixel_cnt[8:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_tal_pixel_cnt <= 'b0;
    else if(state==IDLE)
        v_tal_pixel_cnt <= 'b0;
    else if(v_change_port==1 && v_port_id==read_port_max[4:3])
        v_tal_pixel_cnt <= v_tal_pixel_cnt + 4'd8;

//v_change_row
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_change_row <= 'b0;
    else if(state==RD2 && v_row_pixel_cnt==6 && v_port_id==read_port_max[4:3])
    begin
        if(I_cfg_scan_mode==2)
            v_change_row <= (v_tal_pixel_cnt[6:3]==4'b1111);
        else
            v_change_row <= (v_tal_pixel_cnt[5:3]==3'b111);
    end
    else
        v_change_row <= 'b0;

//v_last_pixel
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_last_pixel <= 'b0;
    else if(state==IDLE)
        v_last_pixel <= 'b0;
    else if(state==RD2 && v_row_pixel_cnt==3'd6 && v_port_id==read_port_max[4:3]
            && v_tal_pixel_cnt[8:3]==read_pixel_count_minus_1[8:3])
        v_last_pixel <= 1'b1;
//***********************************************************
//wr_reg_cnt[3:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        wr_reg_cnt <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        wr_reg_cnt <= 'b0;
    else if(read_sr[RL]==1)
        wr_reg_cnt <= wr_reg_cnt + 1'b1;

//rd_reg_cnt[3:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        rd_reg_cnt <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        rd_reg_cnt <= 'b0;
    else if(read_sr[RL+8]==1)
        rd_reg_cnt <= rd_reg_cnt + 1'b1;

//d0~d15[23:0]
always@(posedge I_sdram_clk)
    if(read_sr[RL]==1)
    case(wr_reg_cnt)
        'd0 :   d0  <= word1;
        'd1 :   d1  <= word1;
        'd2 :   d2  <= word1;
        'd3 :   d3  <= word1;
        'd4 :   d4  <= word1;
        'd5 :   d5  <= word1;
        'd6 :   d6  <= word1;
        'd7 :   d7  <= word1;

        'd8 :   d8  <= word1;
        'd9 :   d9  <= word1;
        'd10:   d10 <= word1;
        'd11:   d11 <= word1;
        'd12:   d12 <= word1;
        'd13:   d13 <= word1;
        'd14:   d14 <= word1;
        'd15:   d15 <= word1;
    endcase

//v_ram_wren[7:0]
//v_ram_addr[7:0]
//v_ram_data[23:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_ram_wren <= 'b0;
    else if(read_sr[RL+8]==1)
    begin
        v_ram_wren <= (1'b1 << v_real_port_index[4:2]);
        v_ram_addr <= {read_buf_index,v_real_port_index[1:0],v_tal_pixel_cnt_1_tmp1[8:3]};

        case(rd_reg_cnt)
            'd0 :   v_ram_data <= {d7[2 :0 ],d6[2 :0 ],d5[2 :0 ],d4[2 :0 ],d3[2 :0 ],d2[2 :0 ],d1[2 :0 ],d0[2 :0 ]};
            'd1 :   v_ram_data <= {d7[5 :3 ],d6[5 :3 ],d5[5 :3 ],d4[5 :3 ],d3[5 :3 ],d2[5 :3 ],d1[5 :3 ],d0[5 :3 ]};
            'd2 :   v_ram_data <= {d7[8 :6 ],d6[8 :6 ],d5[8 :6 ],d4[8 :6 ],d3[8 :6 ],d2[8 :6 ],d1[8 :6 ],d0[8 :6 ]};
            'd3 :   v_ram_data <= {d7[11:9 ],d6[11:9 ],d5[11:9 ],d4[11:9 ],d3[11:9 ],d2[11:9 ],d1[11:9 ],d0[11:9 ]};
            'd4 :   v_ram_data <= {d7[14:12],d6[14:12],d5[14:12],d4[14:12],d3[14:12],d2[14:12],d1[14:12],d0[14:12]};
            'd5 :   v_ram_data <= {d7[17:15],d6[17:15],d5[17:15],d4[17:15],d3[17:15],d2[17:15],d1[17:15],d0[17:15]};
            'd6 :   v_ram_data <= {d7[20:18],d6[20:18],d5[20:18],d4[20:18],d3[20:18],d2[20:18],d1[20:18],d0[20:18]};
            'd7 :   v_ram_data <= {d7[23:21],d6[23:21],d5[23:21],d4[23:21],d3[23:21],d2[23:21],d1[23:21],d0[23:21]};

            'd8 :   v_ram_data <= {d15[2 :0 ],d14[2 :0 ],d13[2 :0 ],d12[2 :0 ],d11[2 :0 ],d10[2 :0 ],d9[2 :0 ],d8[2 :0 ]};
            'd9 :   v_ram_data <= {d15[5 :3 ],d14[5 :3 ],d13[5 :3 ],d12[5 :3 ],d11[5 :3 ],d10[5 :3 ],d9[5 :3 ],d8[5 :3 ]};
            'd10:   v_ram_data <= {d15[8 :6 ],d14[8 :6 ],d13[8 :6 ],d12[8 :6 ],d11[8 :6 ],d10[8 :6 ],d9[8 :6 ],d8[8 :6 ]};
            'd11:   v_ram_data <= {d15[11:9 ],d14[11:9 ],d13[11:9 ],d12[11:9 ],d11[11:9 ],d10[11:9 ],d9[11:9 ],d8[11:9 ]};
            'd12:   v_ram_data <= {d15[14:12],d14[14:12],d13[14:12],d12[14:12],d11[14:12],d10[14:12],d9[14:12],d8[14:12]};
            'd13:   v_ram_data <= {d15[17:15],d14[17:15],d13[17:15],d12[17:15],d11[17:15],d10[17:15],d9[17:15],d8[17:15]};
            'd14:   v_ram_data <= {d15[20:18],d14[20:18],d13[20:18],d12[20:18],d11[20:18],d10[20:18],d9[20:18],d8[20:18]};
            'd15:   v_ram_data <= {d15[23:21],d14[23:21],d13[23:21],d12[23:21],d11[23:21],d10[23:21],d9[23:21],d8[23:21]};
        endcase
    end
    else
        v_ram_wren <= 'b0;

//v_real_port_index[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_real_port_index <= 'b0;
    else if(read_sr[RL+7]==1)
        v_real_port_index <= I_port_map_q;

//v_port_map_rden
//v_port_map_addr[7:0]
assign v_port_map_rden = read_sr[RL+6];
assign v_port_map_addr = {3'b100,v_port_id_1,v_virtual_port_index};

//v_virtual_port_index[2:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_virtual_port_index <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        v_virtual_port_index <= 'b0;
    else if(read_sr[RL+6]==1)
        v_virtual_port_index <= v_virtual_port_index + 1'b1;
//***********************************************************
//v_row_pixel_cnt_1[2:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_row_pixel_cnt_1 <= 'b0;
    else if((state==REQ_MUX && last_state==IDLE) || v_change_port_1==1)
        v_row_pixel_cnt_1 <= 'b0;
    else if(read_sr[RL+6]==1)
        v_row_pixel_cnt_1 <= v_row_pixel_cnt_1 + 1'b1;

//v_change_port_1
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_change_port_1 <= 'b0;
    else if(read_sr[RL+6]==1 && v_row_pixel_cnt_1==3'd6)
        v_change_port_1 <= 1'b1;
    else
        v_change_port_1 <= 'b0;

//v_port_id_1[1:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_port_id_1 <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        v_port_id_1 <= 'b0;
    else if(v_change_port_1==1)
    begin
        if(v_port_id_1==read_port_max[4:3])
            v_port_id_1 <= 'b0;
        else
            v_port_id_1 <= v_port_id_1 + 1'b1;
    end

//v_tal_pixel_cnt_1[8:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_tal_pixel_cnt_1 <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        v_tal_pixel_cnt_1 <= 'b0;
    else if(v_change_port_1==1 && v_port_id_1==read_port_max[4:3])
        v_tal_pixel_cnt_1 <= v_tal_pixel_cnt_1 + 4'd8;

//v_tal_pixel_cnt_1_tmp0[8:0]
//v_tal_pixel_cnt_1_tmp1[8:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
    begin
        v_tal_pixel_cnt_1_tmp0 <= 'b0;
        v_tal_pixel_cnt_1_tmp1 <= 'b0;
    end
    else
    begin
        v_tal_pixel_cnt_1_tmp0 <= v_tal_pixel_cnt_1;
        v_tal_pixel_cnt_1_tmp1 <= v_tal_pixel_cnt_1_tmp0;
    end
//***********************************************************
//O_port_map_rden
//O_port_map_addr[7:0]
assign O_port_map_rden = (landscape_mode==1) ? h_port_map_rden : v_port_map_rden;
assign O_port_map_addr = (landscape_mode==1) ? h_port_map_addr : v_port_map_addr;

//O_ram_wclk
//O_ram_wren[7:0]
//O_ram_addr[8:0]
//O_ram_data[23:0]
assign O_ram_wclk = I_sdram_clk;
assign O_ram_wren = (landscape_mode==1) ? h_ram_wren : v_ram_wren;
assign O_ram_addr = (landscape_mode==1) ? h_ram_addr : v_ram_addr;
assign O_ram_data = (landscape_mode==1) ? h_ram_data : v_ram_data;
//***********************************************************
assign O_buf_frm_start = buf_frm_start;
assign O_buf_sel       = read_buf_index;
assign O_buf_vld       = (landscape_mode==1) ? h_buf_vld      : v_buf_vld;
assign O_buf_port      = (landscape_mode==1) ? h_buf_port     : v_buf_port;
assign O_buf_port_end  = (landscape_mode==1) ? h_buf_port_end : v_buf_port_end;
assign O_buf_data      = (landscape_mode==1) ? h_buf_data     : v_buf_data;

//buf_frm_start
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        buf_frm_start <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        buf_frm_start <= 1'b1;
    else
        buf_frm_start <= 'b0;

//h_buf_vld
//h_buf_port[4:0]
//h_buf_data[23:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        h_buf_vld  <= 'b0;
    else if(read_sr[RL]==1)
    begin
        h_buf_vld  <= 1'b1;
        h_buf_port <= real_port_index;
        h_buf_data <= word1;
    end
    else
        h_buf_vld  <= 'b0;

//h_buf_port_end
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        h_buf_port_end <= 'b0;
    else if(read_sr[RL]==1 && virtual_port_index_tmp1=={read_port_max[4:2],2'b11})
        h_buf_port_end <= 1'b1;
    else
        h_buf_port_end <= 'b0;

//v_buf_vld
//v_buf_port[4:0]
//v_buf_data[23:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_buf_vld  <= 'b0;
    else if(read_sr[RL+8]==1)
    begin
        v_buf_vld  <= 1'b1;
        v_buf_port <= v_real_port_index;

        case(rd_reg_cnt)
            'd0 :   v_buf_data <= {d7[2 :0 ],d6[2 :0 ],d5[2 :0 ],d4[2 :0 ],d3[2 :0 ],d2[2 :0 ],d1[2 :0 ],d0[2 :0 ]};
            'd1 :   v_buf_data <= {d7[5 :3 ],d6[5 :3 ],d5[5 :3 ],d4[5 :3 ],d3[5 :3 ],d2[5 :3 ],d1[5 :3 ],d0[5 :3 ]};
            'd2 :   v_buf_data <= {d7[8 :6 ],d6[8 :6 ],d5[8 :6 ],d4[8 :6 ],d3[8 :6 ],d2[8 :6 ],d1[8 :6 ],d0[8 :6 ]};
            'd3 :   v_buf_data <= {d7[11:9 ],d6[11:9 ],d5[11:9 ],d4[11:9 ],d3[11:9 ],d2[11:9 ],d1[11:9 ],d0[11:9 ]};
            'd4 :   v_buf_data <= {d7[14:12],d6[14:12],d5[14:12],d4[14:12],d3[14:12],d2[14:12],d1[14:12],d0[14:12]};
            'd5 :   v_buf_data <= {d7[17:15],d6[17:15],d5[17:15],d4[17:15],d3[17:15],d2[17:15],d1[17:15],d0[17:15]};
            'd6 :   v_buf_data <= {d7[20:18],d6[20:18],d5[20:18],d4[20:18],d3[20:18],d2[20:18],d1[20:18],d0[20:18]};
            'd7 :   v_buf_data <= {d7[23:21],d6[23:21],d5[23:21],d4[23:21],d3[23:21],d2[23:21],d1[23:21],d0[23:21]};

            'd8 :   v_buf_data <= {d15[2 :0 ],d14[2 :0 ],d13[2 :0 ],d12[2 :0 ],d11[2 :0 ],d10[2 :0 ],d9[2 :0 ],d8[2 :0 ]};
            'd9 :   v_buf_data <= {d15[5 :3 ],d14[5 :3 ],d13[5 :3 ],d12[5 :3 ],d11[5 :3 ],d10[5 :3 ],d9[5 :3 ],d8[5 :3 ]};
            'd10:   v_buf_data <= {d15[8 :6 ],d14[8 :6 ],d13[8 :6 ],d12[8 :6 ],d11[8 :6 ],d10[8 :6 ],d9[8 :6 ],d8[8 :6 ]};
            'd11:   v_buf_data <= {d15[11:9 ],d14[11:9 ],d13[11:9 ],d12[11:9 ],d11[11:9 ],d10[11:9 ],d9[11:9 ],d8[11:9 ]};
            'd12:   v_buf_data <= {d15[14:12],d14[14:12],d13[14:12],d12[14:12],d11[14:12],d10[14:12],d9[14:12],d8[14:12]};
            'd13:   v_buf_data <= {d15[17:15],d14[17:15],d13[17:15],d12[17:15],d11[17:15],d10[17:15],d9[17:15],d8[17:15]};
            'd14:   v_buf_data <= {d15[20:18],d14[20:18],d13[20:18],d12[20:18],d11[20:18],d10[20:18],d9[20:18],d8[20:18]};
            'd15:   v_buf_data <= {d15[23:21],d14[23:21],d13[23:21],d12[23:21],d11[23:21],d10[23:21],d9[23:21],d8[23:21]};
        endcase
    end
    else
        v_buf_vld  <= 'b0;

//v_buf_port_index[4:0]
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_buf_port_index <= 'b0;
    else if(state==REQ_MUX && last_state==IDLE)
        v_buf_port_index <= 'b0;
    else if(read_sr[RL+8]==1)
    begin
        if(v_buf_port_index=={read_port_max[4:3],3'b111})
            v_buf_port_index <= 'b0;
        else
            v_buf_port_index <= v_buf_port_index + 1'b1;
    end

//v_buf_port_end
always@(posedge I_sdram_clk or negedge I_rst_n)
    if(!I_rst_n)
        v_buf_port_end <= 'b0;
    else if(read_sr[RL+8]==1 && v_buf_port_index=={read_port_max[4:3],3'b111})
        v_buf_port_end <= 1'b1;
    else
        v_buf_port_end <= 'b0;
//***********************************************************
endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
